#include "timingwhell.h"
#include "logstream.h"

using namespace xyu::net;

TimingWheel::TimingWheel()
    : wheels_(4)
{
    wheels_[TimingType::Second].resize(60);
    wheels_[TimingType::Minute].resize(60);
    wheels_[TimingType::Hour].resize(24);
    wheels_[TimingType::Day].resize(30);
}

TimingWheel::~TimingWheel()
{
}

void TimingWheel::InsertEntry(uint32_t delay, EntryPtr entry)
{
    if (delay <= 0)
    {
        entry.reset();
    }

    if (delay < MinuteType)
    {
        InsertSecondEntry(delay, entry);
    }
    else if (delay < HourType)
    {
        InsertMinuteEntry(delay, entry);
    }
    else if (delay < DayType)
    {
        InsertHourEntry(delay, entry);
    }
    else
    {
        auto day= delay / DayType;
        if (day > 30)
        {
            LOG_ERROR << "is is not support > 30 day!";
            return;
        }

        InsertDayEntry(delay, entry);
    }
}

// 转动时间轮
void TimingWheel::OnTimer(int64_t now)
{
    if (last_ts_ == 0)
    {
        last_ts_ = now;
    }

    if (now - last_ts_ < 1000)
    {
        return;
    }

    last_ts_ = now;
    tick_ ++ ;
    PopUp(wheels_[TimingType::Second]);

    if (tick_ % MinuteType == 0)
    {
        PopUp(wheels_[TimingType::Minute]);
    }
    else if (tick_ % HourType == 0)
    {
        PopUp(wheels_[TimingType::Hour]);
    }
    else if (tick_ % DayType == 0)
    {
        PopUp(wheels_[TimingType::Day]);
    }
}

void TimingWheel::PopUp(Wheel &pop)
{
    WheelEntry tmp;
    pop.front().swap(tmp);
    pop.pop_front();
    pop.push_back(WheelEntry{});
}

void TimingWheel::RunAfter(double delay, const Func &cb)
{
    CallbackEntryPtr callback = std::make_shared<CallbackEntry>([cb](){
        cb();
    });

    InsertEntry(delay, callback);
}

void TimingWheel::RunAfter(double delay, Func &&cb)
{
    CallbackEntryPtr callback = std::make_shared<CallbackEntry>([cb](){
        cb();
    });

    InsertEntry(delay, callback);
}

void TimingWheel::RunEvery(double interval, const Func &cb)
{
    CallbackEntryPtr callback = std::make_shared<CallbackEntry>([this, cb, interval](){
        cb();
        RunEvery(interval, cb);
    });

    InsertEntry(interval, callback);
}

void TimingWheel::RunEvery(double interval, Func &&cb)
{
    CallbackEntryPtr callback = std::make_shared<CallbackEntry>([this, cb, interval](){
        cb();
        RunEvery(interval, cb);
    });

    InsertEntry(interval, callback);
}

void TimingWheel::InsertSecondEntry(uint32_t delay, EntryPtr entry)
{
    wheels_[TimingType::Second][delay - 1].emplace(entry);
}

void TimingWheel::InsertMinuteEntry(uint32_t delay, EntryPtr entry)
{
    auto minute = delay / MinuteType;
    auto second = delay % MinuteType;
    CallbackEntryPtr newEvPtr = std::make_shared<CallbackEntry>([this, second, entry](){
        InsertEntry(second, entry);
    });
    wheels_[TimingType::Minute][minute - 1].emplace(newEvPtr);
}

void TimingWheel::InsertHourEntry(uint32_t delay, EntryPtr entry)
{
    auto hour = delay / HourType;
    auto second = delay % HourType;
    CallbackEntryPtr newEvPtr = std::make_shared<CallbackEntry>([this, second, entry](){
        InsertEntry(second, entry);
    });
    wheels_[TimingType::Hour][hour - 1].emplace(newEvPtr);
}

void TimingWheel::InsertDayEntry(uint32_t delay, EntryPtr entry)
{
    auto day = delay / DayType;
    auto second = delay % DayType;
    CallbackEntryPtr newEvPtr = std::make_shared<CallbackEntry>([this, second, entry](){
        InsertEntry(second, entry);
    });
    wheels_[TimingType::Day][day - 1].emplace(newEvPtr);
}

